Ruby parser for Blitz3D file format
===================================

What's Blitz3D?
---------------
Blitz3D is a 3D file format.  It stores meshes, animation skeletons, keyframes, among other things.  (As for textures: it references external files.)
It is an export format (i.e. app-neutral and not suitable for editing, e.g. does not support quads).

Blitz3D format is simplistic, hierarchical, binary, and uncompressed.  Its filename extension is `.b3d`.

For more information, just read the specification.  It is very well written
(well, at least the overall structure is), so there is no point for me to re-explain it.

Blitz3D format is used by [Minetest][].  [SuperTuxKart][] also barely uses Blitz3D (they mainly use a homemade SPM format).
It doesn't seem to be a widespread format.
In fact, the only documentation I could find was the `b3dfile_specs.txt` circulated in parser repos.
However, Irrlicht documentation claims that Blitz3D is widely supported.

[minetest]:     https://www.minetest.net/
[supertuxkart]: https://supertuxkart.net/Main_Page

Other available software
------------------------
I haven't looked into this extensively.  So far, I've found the following

- [Irrlicht][] (C++) ([`ISceneManager::getMesh`](https://irrlicht.sourceforge.io/docu/classirr_1_1scene_1_1_i_scene_manager.html#a63894c3f3d46cfc385116f1705935e03))
- Blender Import-Export [plugin][pynew]<sup>[[forks][pynewfork],[old][pyold],[oldforks][pyoldfork]]</sup>(Python)
- fragMOTION (proprietary editor)
- this repo (Ruby)

LibG3D recognizes a `.b3d` format but [it isn't Blitz3D](https://salsa.debian.org/ecsv/libg3d/-/blob/ab2944613408a5a867ecae69ce16099f2aad52da/plugins/import/imp_3dmf/imp_3dmf.c#L70).

Assimp (used by OGRE) does not support `.b3d`.


[irrlicht]:  https://irrlicht.sourceforge.io/
[pyold]:     https://github.com/minetest/B3DExport
[pyoldfork]: https://github.com/minetest/B3DExport/network/members
[pynew]:     https://github.com/GreenXenith/io_scene_b3d
[pynewfork]: https://github.com/GreenXenith/io_scene_b3d/network/members


Why reinvent the wheel?
-----------------------
First of all, this is a by-product.  In order to understand the (1600+ lines!) Blender plugin for `.b3d`,
I wrote a short script to analyze some example data.
Soon, it became evident that the partial parser is easily adaptable into a full parser.
(That's how well-designed Blitz3D is — if you can parse a few chunks,
you can parse the entire file with minimal additional effort.)


That said, this Ruby parser does provide some unique features that others don't.

The core of this library is a meta-parser + a schema.
The meta-parser handles the universal structure of Blitz3D: chunks, repetitions, primitives (int32, float32, NUL-terminated string).
The schema tells the parser exactly what to expect.  This combo leads to code deduplication and high configurability.

Errors are reported through exceptions.  On error, the entire parsing aborts.  There is no error recovery and no data is returned.


Advantages:
- somewhat documented
- flexible
  - change output format by registering post-processors
  - add new types of chunks / new flags / etc by editing a global variable, no need to touch the parser
- basic error messages
- (almost strictly) verifying parser
- pure parser (stateless + no side effects)

Disadvantages:
- NOT tested extensively. use at your own risk
- loads entire file before parsing
- potentially slower than other libraries
- no plan for long term maintenance
- ugly error handling (current version)
- uses beta syntax, may break in later versions of Ruby

Neutral features:
- written in Ruby (can't use this for Blender)
- general purpose interface (have to adapt it for specific applications)
- source code may or may not be readable




Dependencies, etc. boring stuffs
--------------------------------
Tested in Ruby 2.7.  This script uses pattern matching so older versions are likely going to fail.

Only supports Blitz3D v0.01.  (That said, v0.x should be easily parsable by editing the schema.  The real question might be, will there ever be a v0.02 ?)


Blitz3D v0.01 specification is placed in Public Domain.

blitz3d-parser-ruby is licensed under GPLv2+.  [View license](https://www.gnu.org/licenses/old-licenses/gpl-2.0.html)

This README document is licensed under Creative Commons BY-SA 4.0.  [View license](https://creativecommons.org/licenses/by-sa/4.0/)



What's Next
-----------
TODO list:
- fill in missing documentation
- option to turn off schema (still verify chunk size, but don't verify chunk relations or order)
- incremental I/O (The parser accesses buffer linearly, so partial loads should be as easy as adding a buffer wrapper)
  - the resulting data will still be in one piece.  push parsing is probably possible by setting custom post-processors but I haven't checked
- change to signed ints (currently -1 is parsed as 4294967295)

Low priority:
- write an exporter
- test cases
- verify Blitz3D version number
- ignore unrecognized chunks, as required by the spec
- improve error reporting

